/*
 * MIT License
 *
 * Copyright (c) 2024-2048 冰羽
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package cn.star.framework.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.star.framework.core.Constant;
import java.lang.reflect.Array;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 日期转换处理<br>
 *
 * @author zhaoweiping
 *     <p style='color: red'>Created on 2024-09-23 11:06:00
 * @since 3.0.0
 */
public class DateConvertHelper extends DatePattern {

  /**
   * 转换方法
   *
   * @param type 目标数据类型<br>
   *     <h3>Support Type</h3>
   *     <ul>
   *       <li>{@link Date}
   *       <li>{@link LocalTime}
   *       <li>{@link LocalDate}
   *       <li>{@link LocalDateTime}
   *       <li>{@link Timestamp}
   *       <li>{@link Instant}
   *     </ul>
   *
   * @param value 待转换的值<br>
   *     <h3>Support Type</h3>
   *     <ul>
   *       <li>{@link Long} 时间戳，支持秒级和毫秒级，自动判断
   *       <li>{@link String} 日期时间格式字符串，和format相关
   *       <li>{@link Array} 可能装载了多个`日期类型`的时间戳或字符串数据，集合中的数据默认转换为{@link Timestamp}
   *       <li>{@link List} 可能装载了多个`日期类型`的时间戳或字符串数据，集合中的数据默认转换为{@link Timestamp}
   *     </ul>
   *
   * @return 返回和{@link convert#value}的类型相关，单值则返回转换后的日期单值，若是集合则返回日期集合
   */
  public static Object convert(Class<?> type, Object value) {
    return convert(type, value, null);
  }

  /**
   * 转换方法
   *
   * @param type 目标数据类型<br>
   *     <h3>Support Type</h3>
   *     <ul>
   *       <li>{@link Date}
   *       <li>{@link LocalTime}
   *       <li>{@link LocalDate}
   *       <li>{@link LocalDateTime}
   *       <li>{@link Timestamp}
   *       <li>{@link Instant}
   *     </ul>
   *
   * @param value 待转换的值<br>
   *     <h3>Support Type</h3>
   *     <ul>
   *       <li>{@link Long} 时间戳，支持秒级和毫秒级，自动判断
   *       <li>{@link String} 日期时间格式字符串，和format相关
   *       <li>{@link Array} 可能装载了多个`日期类型`的时间戳或字符串数据，集合中的数据默认转换为{@link Timestamp}
   *       <li>{@link List} 可能装载了多个`日期类型`的时间戳或字符串数据，集合中的数据默认转换为{@link Timestamp}
   *     </ul>
   *
   * @param format 格式化日期的方法
   * @return 返回和{@link convert#value}的类型相关，单值则返回转换后的日期单值，若是集合则返回日期集合
   */
  @SuppressWarnings({"unchecked", "rawtypes"})
  public static Object convert(Class<?> type, Object value, String format) {
    if (type == null || value == null) {
      return null;
    }

    if (isLongType(value)) {
      Long timestamp = (Long) value;
      return toType(type, timestamp);
    } else if (value instanceof Iterable) {
      List list = CollectionUtil.newArrayList((Iterable) value);
      return toTypes(Timestamp.class, list, format);
    } else if (value.getClass().isArray()) {
      List list = CollectionUtil.toList((Object[]) value);
      return toTypes(Timestamp.class, list, format);
    } else {
      return toType(type, value.toString(), format);
    }
  }

  private static Object toType(Class<?> type, Long value) {
    // 支持秒级别和毫秒级别，自动判断
    Long milliseconds =
        String.valueOf(value).length() == Constant.NUMBER_THIRTEEN
            ? Long.parseLong(value.toString())
            : Long.parseLong(value.toString()) * Constant.NUMBER_ONE_THOUSAND;
    return getTypeValue(type, milliseconds, null);
  }

  private static boolean isLongType(Object value) {
    return value instanceof Long;
  }

  private static Object getTypeValue(Class<?> type, Object value, String format) {
    if (type.getName().equals(Date.class.getName())) {
      return getDate(value, format);
    } else if (type.getName().equals(LocalTime.class.getName())) {
      return getLocalTime(value, format);
    } else if (type.getName().equals(LocalDate.class.getName())) {
      return getLocalDate(value, format);
    } else if (type.getName().equals(LocalDateTime.class.getName())) {
      return getLocalDateTime(value, format);
    } else if (type.getName().equals(Timestamp.class.getName())) {
      return getTimestamp(value, format);
    } else if (type.getName().equals(Instant.class.getName())) {
      return getInstant(value, format);
    } else {
      throw new RuntimeException("No Converter for type [" + type.getName() + "]");
    }
  }

  private static Date getDate(Object value, String format) {
    if (isLongType(value)) {
      return new Date((Long) value);
    }
    try {
      SimpleDateFormat simple =
          new SimpleDateFormat(StrUtil.isNotBlank(format) ? format : NORM_DATE_PATTERN);
      return simple.parse(value.toString());
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  private static LocalTime getLocalTime(Object value, String format) {
    return DateUtil.toLocalDateTime(getDate(value, format)).toLocalTime();
  }

  private static LocalDate getLocalDate(Object value, String format) {
    return DateUtil.toLocalDateTime(getDate(value, format)).toLocalDate();
  }

  private static LocalDateTime getLocalDateTime(Object value, String format) {
    return DateUtil.toLocalDateTime(getDate(value, format));
  }

  private static Timestamp getTimestamp(Object value, String format) {
    if (isLongType(value)) {
      return new Timestamp((Long) value);
    }
    try {
      return Timestamp.valueOf(value.toString());
    } catch (Exception e) {
      // 会丢失纳秒级的精度
      return Timestamp.valueOf(getLocalDateTime(value, format));
    }
  }

  private static Instant getInstant(Object value, String format) {
    if (isLongType(value)) {
      return Instant.ofEpochMilli((Long) value);
    }
    DateTimeFormatter formatter =
        DateTimeFormatter.ofPattern(StrUtil.isNotBlank(format) ? format : NORM_DATETIME_MS_PATTERN);
    return LocalDateTime.parse(value.toString(), formatter).toInstant(ZoneOffset.UTC);
  }

  private static Object toType(Class<?> type, String value, String format) {
    // 有可能还是Long数据类型
    try {
      return toType(type, Long.valueOf(value));
    } catch (Exception e) {
      if (e instanceof NumberFormatException) {
        // 此时判断不是Long，使用字符串处理
        return getTypeValue(type, value, format);
      } else {
        throw e;
      }
    }
  }

  @SuppressWarnings("all")
  private static List<Object> toTypes(Class<?> type, List<String> value, String formats) {
    return value.stream().map(i -> toType(type, i, formats)).collect(Collectors.toList());
  }
}

// note: 判断是否子类 isAssignableFrom
